﻿using Core.FrameWork.Commons.ORM.Abstraction;
using Core.FrameWork.Commons.ORM.Enums;
using Core.FrameWork.Commons.ORM.Models;
using Core.FrameWork.Commons.Pages;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Core.FrameWork.Commons.ORM.UnitWork
{
    public class UnitWork<TDbContext> : IUnitWork<TDbContext> where TDbContext : DbContext
    {
        #region EF构造函数及基本配置
        private TDbContext _dbContext;
        /// <summary>
        /// EF 上下文接口，可读可写
        /// </summary>
        public virtual TDbContext DbContext
        {
            get { return _dbContext; }
        }

        /// <summary>
        /// 构造方法
        /// </summary>
        public UnitWork()
        {
            _dbContext = (TDbContext)DbContextFactory.CreateContext<TDbContext>(WriteAndReadEnum.Write);
        }


        #endregion
        public int Save()
        {
            return _dbContext.SaveChanges();
        }

        public async Task<int> SaveAsync()
        {
            return await _dbContext.SaveChangesAsync();
        }
        public IQueryable<T> Find<T>(Expression<Func<T, bool>> @where = null) where T : class
        {
            return DbContext.Set<T>().AsNoTracking().Where(@where);
        }

        /// <summary>
        /// 根据条件统计数量Count() 
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual int Count<T>(Expression<Func<T, bool>> @where = null) where T : class
        {
            return Find(@where).Count();
        }

        /// <summary>
        /// 根据条件统计数量Count()
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual async Task<int> CountAsync<T>(Expression<Func<T, bool>> @where = null) where T : class
        {
            return await Find(@where).CountAsync(where);
        }
        /// <summary>
        /// 是否存在,存在返回true，不存在返回false
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>

        public virtual bool Exist<T>(Expression<Func<T, bool>> @where = null) where T : class
        {
            return Find(@where).Any();
        }
        /// <summary>
        /// 是否存在,存在返回true，不存在返回false
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual async Task<bool> ExistAsync<T>(Expression<Func<T, bool>> @where = null) where T : class
        {
            return await Find(where).AnyAsync();
        }

        /// <summary>
        /// 获取单个实体。建议：如需使用Include和ThenInclude请重载此方法。
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual T GetSingleOrDefault<T>(Expression<Func<T, bool>> @where) where T : class
        {
            return Find(@where).FirstOrDefault();
        }

        /// <summary>
        /// 获取单个实体。建议：如需使用Include和ThenInclude请重载此方法。
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual async Task<T> GetSingleOrDefaultAsync<T>(Expression<Func<T, bool>> @where) where T : class
        {
            return await Find(@where).FirstOrDefaultAsync();
        }
        /// <summary>
        /// 获取实体列表(排序)。建议：如需使用Include和ThenInclude请重载此方法。
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual IList<T> Get<T>(Expression<Func<T, bool>> @where) where T : class
        {
            return Find(@where).ToList();
        }
        public virtual async Task<IList<T>> GetAsync<T>(Expression<Func<T, bool>> @where) where T : class
        {
            return await Find(@where).ToListAsync();
        }

        /// <summary>
        /// 获取实体列表(排序)。建议：如需使用Include和ThenInclude请重载此方法。
        /// </summary>
        /// <param name="where"></param>
        /// <returns></returns>
        public virtual IList<T> Get<T>(Expression<Func<T, bool>> @where, bool[] asc, params Expression<Func<T, object>>[] @orderby) where T : class
        {
            var filter = Find(where);
            if (orderby != null)
            {
                for (int i = 0; i < @orderby.Length; i++)
                {
                    if (i == 0)
                    {
                        filter = asc[i] ? filter.OrderBy(@orderby[i]) : filter.OrderByDescending(@orderby[i]);
                    }
                    else
                        filter = asc[i] ? ((IOrderedQueryable<T>)filter).ThenBy(@orderby[i]) : ((IOrderedQueryable<T>)filter).ThenByDescending(@orderby[i]);

                }
            }
            return filter.ToList();
        }

        public virtual async Task<IList<T>> GetAsync<T>(Expression<Func<T, bool>> @where, bool[] asc, params Expression<Func<T, object>>[] @orderby) where T : class
        {
            var filter = Find(where);
            if (orderby != null)
            {
                for (int i = 0; i < @orderby.Length; i++)
                {
                    if (i == 0)
                    {
                        filter = asc[i] ? filter.OrderBy(@orderby[i]) : filter.OrderByDescending(@orderby[i]);
                    }
                    else
                        filter = asc[i] ? ((IOrderedQueryable<T>)filter).ThenBy(@orderby[i]) : ((IOrderedQueryable<T>)filter).ThenByDescending(@orderby[i]);

                }
            }
            return await filter.ToListAsync();
        }
        /// <summary>
        ///  分页获取实体列表。建议：如需使用Include和ThenInclude请重载此方法。
        /// </summary>
        /// <param name="where">查询条件</param>
        /// <param name="pagerInfo">分页信息</param>
        /// <param name="asc">排序方式</param>
        /// <param name="orderby">排序字段</param>
        /// <returns></returns>
        public virtual IEnumerable<T> GetByPagination<T>(Expression<Func<T, bool>> @where, PagerInfo pagerInfo, bool[] asc, params Expression<Func<T, object>>[] @orderby) where T : class
        {
            var filter = Find(where);
            if (orderby != null)
            {
                for (int i = 0; i < @orderby.Length; i++)
                {
                    if (i == 0)
                    {
                        filter = asc[i] ? filter.OrderBy(@orderby[i]) : filter.OrderByDescending(@orderby[i]);
                    }
                    else
                        filter = asc[i] ? ((IOrderedQueryable<T>)filter).ThenBy(@orderby[i]) : ((IOrderedQueryable<T>)filter).ThenByDescending(@orderby[i]);

                }
            }
            pagerInfo.RecordCount = filter.Count();
            return filter.Skip(pagerInfo.PageSize * (pagerInfo.CurrenetPageIndex - 1)).Take(pagerInfo.PageSize);
        }
        public virtual async Task<IList<T>> GetByPaginationAsync<T>(Expression<Func<T, bool>> @where, PagerInfo pagerInfo, bool[] asc,
    params Expression<Func<T, object>>[] @orderby) where T : class
        {
            var filter = Find(where);
            if (orderby != null)
            {
                for (int i = 0; i < @orderby.Length; i++)
                {
                    if (i == 0)
                    {
                        filter = asc[i] ? filter.OrderBy(@orderby[i]) : filter.OrderByDescending(@orderby[i]);
                    }
                    else
                        filter = asc[i] ? ((IOrderedQueryable<T>)filter).ThenBy(@orderby[i]) : ((IOrderedQueryable<T>)filter).ThenByDescending(@orderby[i]);

                }
            }
            pagerInfo.RecordCount = await filter.CountAsync();
            return await filter.Skip(pagerInfo.PageSize * (pagerInfo.CurrenetPageIndex - 1)).Take(pagerInfo.PageSize).ToListAsync();
        }
        /// <summary>
        /// sql语句查询数据集
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
        public List<T> GetBySql<T>(string sql) where T : class
        {
            return DbContext.Set<T>().FromSqlRaw(sql).ToList();
        }
        /// <summary>
        /// sql语句查询数据集，返回输出Dto实体
        /// </summary>
        /// <typeparam name="TView"></typeparam>
        /// <param name="sql"></param>
        /// <returns></returns>
        public List<TView> GetViews<T,TView>(string sql) where T : class
        {
            return DbContext.Set<T>().FromSqlRaw(sql).Cast<TView>().ToList();
        }
        /// <summary>
        /// 查询视图
        /// </summary>
        /// <typeparam name="TView">返回结果对象</typeparam>
        /// <param name="viewName">视图名称</param>
        /// <param name="where">查询条件</param>
        /// <returns></returns>
        public List<TView> GetViews<T, TView>(string viewName, Func<TView, bool> @where) where T : class
        {
            return DbContext.Set<T>().FromSqlRaw($"select * from {viewName}").Cast<TView>().ToList();
        }
        #region 新增
        /// <summary>
        /// 新增实体
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual void Add<T>(T entity) where T : class
        {
            DbContext.Add(entity);
        }
        /// <summary>
        /// 新增实体
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual async Task AddAsync<T>(T entity) where T : class
        {
            await DbContext.AddAsync(entity);
        }
        /// <summary>
        /// 批量新增实体，数量量较多是推荐使用BulkInsert()
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public virtual void AddRange<T>(ICollection<T> entities) where T : class
        {
            DbContext.AddRange(entities);
        }
        /// <summary>
        /// 批量新增实体，数量量较多是推荐使用BulkInsert()
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public virtual async Task AddRangeAsync<T>(ICollection<T> entities) where T : class
        {
            await DbContext.AddRangeAsync(entities);
        }

        #endregion

        #region Update
        /// <summary>
        /// 更新数据实体
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual void Edit<T>(T entity) where T : class
        {
            DbContext.Update<T>(entity);
        }
        /// <summary>
        /// 批量更新数据实体
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public virtual void EditRange<T>(ICollection<T> entities) where T : class
        {
            DbContext.UpdateRange(entities);
        }
        /// <summary>
        /// 更新指定字段的值
        /// </summary>
        /// <param name="model">数据实体</param>
        /// <param name="updateColumns">指定字段</param>
        /// <returns></returns>
        public virtual void Update<T>(T model, params string[] updateColumns) where T : class
        {
            if (updateColumns != null && updateColumns.Length > 0)
            {
                if (DbContext.Entry(model).State == EntityState.Added ||
                    DbContext.Entry(model).State == EntityState.Detached) DbContext.Set<T>().Attach(model);
                foreach (var propertyName in updateColumns)
                {
                    DbContext.Entry(model).Property(propertyName).IsModified = true;
                }
            }
            else
            {
                DbContext.Entry(model).State = EntityState.Modified;
            }
        }
        #endregion
        #region Delete
        /// <summary>
        /// 同步物理删除实体。
        /// </summary>
        /// <param name="entity">实体</param>
        /// <returns></returns>
        public virtual void Delete<T>(T entity) where T : class
        {
            DbContext.Set<T>().Remove(entity);
        }
        /// <summary>
        /// 异步物理删除实体。
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="trans">事务对象</param>
        /// <returns></returns>
        public virtual async Task DeleteAsync<T>(T entity) where T : class
        {
            await Task.Run(() => { DbContext.Set<T>().Remove(entity); });
        }

        #endregion
    }
}
